/**
 * Chrono - Date/Time Utilities
 *
 * @author Chris Pratt
 *
 * 11/26/2001
 */
package com.anodyzed.onyx.util;

import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Date;

public class Chrono {
  protected static final DateFormatSymbols symbols = new DateFormatSymbols();

  /**
   * Constructor
   */
  public Chrono () {
  } //Chrono

  /**
   * Returns a copy of the supplied calendar with the fields up to the specified
   * field cleared, exclusive.  So to clear out the time and leave the date use
   * <code>clearTo(cal,Calendar.DAY_OF_MONTH)</code> or to clear away the
   * seconds to leave the Hours and Minutes, use
   * <code>clearTo(cal,Calendar.MINUTE)</code>
   *
   * @param cal The Calendar to be cleared
   * @param field The Field to be cleared to
   * @return The Cleared Calendar
   */
  @SuppressWarnings("fallthrough")
  public static Calendar clearTo (Calendar cal,int field) {
    Calendar c = (Calendar)cal.clone();
    switch(field) {
      case Calendar.ERA:
        c.clear();
        c.set(Calendar.YEAR,0);
        break;
      case Calendar.YEAR:
        c.clear(Calendar.MONTH);
      case Calendar.MONTH:
        c.set(Calendar.DAY_OF_MONTH,1);
      case Calendar.DAY_OF_MONTH:
        c.set(Calendar.HOUR_OF_DAY,0);
      case Calendar.HOUR:
      case Calendar.HOUR_OF_DAY:
        c.set(Calendar.MINUTE,0);
      case Calendar.MINUTE:
        c.set(Calendar.SECOND,0);
      case Calendar.SECOND:
        c.set(Calendar.MILLISECOND,0);
    }
    return c;
  } //clearTo

  /**
   * Clear the fields of the Date up to the specified field, exclusive
   *
   * @param date The Date to be cleared
   * @param field The Field to be cleared to
   * @return The Cleared Date
   * @see #clearTo(Calendar,int)
   */
  public static Date clearTo (Date date,int field) {
    return clearTo(Convert.toCalendar(date),field).getTime();
  } //clearTo

  /**
   * Test if Calendar A represents a timestamp that is after Calendar B down to 
   * the specified field 
   *
   * @param a The first Calendar 
   * @param b The second Calendar 
   * @param field The Calendar Field 
   * @return true if Calendar A is after Calendar B
   */
  public static boolean after (Calendar a,Calendar b,int field) {
    return clearTo(a,field).after(clearTo(b,field));
  } //after

  /**
   * Test if Calendar/Date A is after Calendar/Date B down to the specified 
   * field 
   *
   * @param a The first Calendar/Date 
   * @param b The second Calendar/Date
   * @param field The Calendar Field 
   * @return true if Calendar/Date A is after Calendar/Date B
   */
  public static boolean after (Object a,Object b,int field) {
    Calendar A = (a instanceof Calendar) ? (Calendar)a : ((a instanceof Date) ? Convert.toCalendar((Date)a) : null);
    Calendar B = (b instanceof Calendar) ? (Calendar)b : ((b instanceof Date) ? Convert.toCalendar((Date)b) : null);
    return (A != null) && (B != null) && after(A,B,field);
  } //after
  
  /**
   * Test if Calendar A represents a timestamp that is before Calendar B down to 
   * the specified field 
   *
   * @param a The first Calendar 
   * @param b The second Calendar 
   * @param field The Calendar Field 
   * @return true if Calendar A is before Calendar B
   */
  public static boolean before (Calendar a,Calendar b,int field) {
    return clearTo(a,field).before(clearTo(b,field));
  } //before

  /**
   * Test if Calendar/Date A is before Calendar/Date B down to the specified 
   * field 
   *
   * @param a The first Calendar/Date 
   * @param b The second Calendar/Date
   * @param field The Calendar Field 
   * @return true if Calendar/Date A is before Calendar/Date B
   */
  public static boolean before (Object a,Object b,int field) {
    Calendar A = (a instanceof Calendar) ? (Calendar)a : ((a instanceof Date) ? Convert.toCalendar((Date)a) : null);
    Calendar B = (b instanceof Calendar) ? (Calendar)b : ((b instanceof Date) ? Convert.toCalendar((Date)b) : null);
    return (A != null) && (B != null) && before(A,B,field);
  } //before

  /**
   * Convenience method to perform Calendar arithmetic
   *
   * @param cal The starting Calendar
   * @param duration The number of units to add (can be negative)
   * @param unit The Calendar units to add
   * @return The sum Calendar
   */
  public static Calendar add (Calendar cal,int duration,int unit) {
    cal.add(unit,duration);
    return cal;
  } //add

  /**
   * Convenience method to perform Date arithmetic
   *
   * @param date The starting date
   * @param duration The number of units to add (can be negative)
   * @param unit The Calendar units to add
   * @return The sum Date
   */
  public static Date add (Date date,int duration,int unit) {
    return add(Convert.toCalendar(date),duration,unit).getTime();
  } //add

  /**
   * Calculate the Unit Change in Time between the two Calendars
   *  for the specified field
   *
   * Unit Change represents the number of times the value has changed or, put
   *  another way, the Library time.  Example:  When you check out a book from
   *  the library at 5:00pm, If you check it in at 9:00am the next morning you
   *  have kept if for 1 day as far as the library is concerned, even though
   *  you have only had it for 16 hours.  It's also true that if you turned it
   *  in at 7:00pm that day, you would still only be charged for 1 day even
   *  though you had the book for 26 hours.  Because the Unit Change in days
   *  was only 1.
   *
   * @param a The first Calendar
   * @param b The second Calendar
   * @param field The Calendar Field
   * @return The Unit Change
   */
  public static int change (Calendar a,Calendar b,int field) {
    Calendar A,B;
    int change = 0;
    if(b.after(a)) {
      A = clearTo(a,field);
      B = clearTo(b,field);
    } else {
      A = clearTo(b,field);
      B = clearTo(a,field);
    }
    while(A.before(B)) {
      A.add(field,1);
      ++change;
    }
    return change;
  } //change

  /**
   * Calculate the Unit Change in Time between two Calendars and/or Dates
   *  for the specified field
   *
   * @param a The First Calendar/Date
   * @param b The Second Calendar/Date
   * @param field The Calendar Field
   * @return The Unit Change
   */
  public static int change (Object a,Object b,int field) {
    Calendar A = (a instanceof Calendar) ? (Calendar)a : ((a instanceof Date) ? Convert.toCalendar((Date)a) : null);
    Calendar B = (b instanceof Calendar) ? (Calendar)b : ((b instanceof Date) ? Convert.toCalendar((Date)b) : null);
    if((A != null) && (B != null)) {
      return change(A,B,field);
    }
    return -1;
  } //change

  /**
   * Calculate the Unit Change in Time between the supplied Calendar or Date
   *  and Now
   *
   * @param a The Calendar/Date
   * @param field The Calendar Field
   * @return The Unit Change
   */
  public static int change (Object a,int field) {
    return change(a,Calendar.getInstance(),field);
  } //change

  /**
   * Get the Abbreviations for the Months of the Year
   *
   * @return 3 Letter Month Abbreviations String Array
   */
  public static String[] getMonthAbbreviations () {
    return symbols.getShortMonths();
  } //getMonthAbbreviations

  /**
   * Get the Names of the Months of the Year
   *
   * @return Month Name String Array
   */
  public static String[] getMonthNames () {
    return symbols.getMonths();
  } //getMonthNames

} //*Chrono
